use serde::Deserialize;
use rust_decimal::Decimal;
use rust_decimal::prelude::*;

/// ========================
/// UserStreamEvent 枚举
/// ========================
#[derive(Debug, Clone)]
pub enum UserStreamEvent {
    AccountUpdate(AccountUpdateEvent),
    OrderUpdate(OrderUpdateEvent),
    MarginCall(MarginCallEvent),
    AccountConfigUpdate(AccountConfigUpdateEvent),
}

/// ========================
/// AccountUpdateEvent（三段）
/// ========================

#[derive(Debug, Deserialize)]
pub struct BalanceRaw {
    pub a: String,
    pub wb: String,
    pub cw: String,
    pub bc: String,
}

impl BalanceRaw {
    pub fn to_model(self) -> Option<Balance> {
        Some(Balance {
            asset: self.a,
            wallet_balance: self.wb.parse().ok()?,
            cross_wallet: self.cw.parse().ok()?,
            balance_change: self.bc.parse().ok()?,
        })
    }
}

#[derive(Debug, Clone)]
pub struct Balance {
    pub asset: String,
    pub wallet_balance: Decimal,
    pub cross_wallet: Decimal,
    pub balance_change: Decimal,
}

#[derive(Debug, Deserialize)]
pub struct PositionRaw {
    pub s: String,
    pub pa: String,
    pub ep: String,
    pub bep: String,
    pub cr: String,
    pub up: String,
    pub mt: String,
    pub iw: String,
    pub ps: String,
}

impl PositionRaw {
    pub fn to_model(self) -> Option<Position> {
        Some(Position {
            symbol: self.s,
            position_amt: self.pa.parse().ok()?,
            entry_price: self.ep.parse().ok()?,
            break_even: self.bep.parse().ok()?,
            realized: self.cr.parse().ok()?,
            unrealized: self.up.parse().ok()?,
            margin_type: self.mt,
            isolated_wallet: self.iw.parse().ok()?,
            position_side: self.ps,
        })
    }
}

#[derive(Debug, Clone)]
pub struct Position {
    pub symbol: String,
    pub position_amt: Decimal,
    pub entry_price: Decimal,
    pub break_even: Decimal,
    pub realized: Decimal,
    pub unrealized: Decimal,
    pub margin_type: String,
    pub isolated_wallet: Decimal,
    pub position_side: String,
}

#[derive(Debug, Deserialize)]
pub struct AccountUpdateDetailRaw {
    pub m: String,
    #[serde(default)]
    pub B: Vec<BalanceRaw>,
    #[serde(default)]
    pub P: Vec<PositionRaw>,
}

impl AccountUpdateDetailRaw {
    pub fn to_model(self) -> Option<AccountUpdateDetail> {
        Some(AccountUpdateDetail {
            reason: self.m,
            balances: self.B.into_iter().filter_map(|b| b.to_model()).collect(),
            positions: self.P.into_iter().filter_map(|p| p.to_model()).collect(),
        })
    }
}

#[derive(Debug, Clone)]
pub struct AccountUpdateDetail {
    pub reason: String,
    pub balances: Vec<Balance>,
    pub positions: Vec<Position>,
}

#[derive(Debug, Deserialize)]
pub struct AccountUpdateRaw {
    pub e: String,
    pub E: u64,
    pub T: u64,
    pub a: AccountUpdateDetailRaw,
}

impl AccountUpdateRaw {
    pub fn to_model(self) -> Option<AccountUpdateEvent> {
        let detail = self.a.to_model()?;
        Some(AccountUpdateEvent {
            event_time: self.E,
            transaction_time: self.T,
            detail,
        })
    }
}

#[derive(Debug, Clone)]
pub struct AccountUpdateEvent {
    pub event_time: u64,
    pub transaction_time: u64,
    pub detail: AccountUpdateDetail,
}

/// ========================
/// OrderUpdateEvent（三段）
/// ========================

#[derive(Debug, Deserialize)]
pub struct OrderUpdateRaw {
    pub e: String,
    pub E: u64,
    pub T: u64,
    pub o: OrderDetailRaw,
}

#[derive(Debug, Deserialize)]
pub struct OrderDetailRaw {
    pub s: String,
    pub c: String,
    pub S: String,
    pub o: String,
    pub f: String,
    pub q: String,
    pub p: String,
    pub ap: String,
    pub sp: String,
    pub x: String,
    pub X: String,
    pub i: u64,
    pub l: String,
    pub z: String,
    pub L: String,
    pub n: String,
    pub N: Option<String>,
    pub T: u64,
    pub t: i64,
    pub b: String,
    pub a: String,
    pub m: bool,
    pub R: bool,
    pub wt: String,
    pub ot: String,
    pub ps: String,
    pub cp: Option<bool>,
    pub rp: String,
    pub pP: Option<bool>,
}

impl OrderDetailRaw {
    pub fn to_model(self) -> Option<OrderUpdateDetail> {
        Some(OrderUpdateDetail {
            symbol: self.s,
            client_order_id: self.c,
            side: self.S,
            order_type: self.o,
            time_in_force: self.f,
            order_qty: self.q.parse().ok()?,
            order_price: self.p.parse().ok()?,
            avg_price: self.ap.parse().ok()?,
            stop_price: self.sp.parse().ok()?,
            execution_type: self.x,
            status: self.X,
            order_id: self.i,
            last_filled_qty: self.l.parse().ok()?,
            cum_filled_qty: self.z.parse().ok()?,
            last_filled_price: self.L.parse().ok()?,
            commission: self.n.parse().unwrap_or_else(|_| Decimal::ZERO),
            commission_asset: self.N,
            trade_time: self.T,
            trade_id: self.t,
            bid_notional: self.b.parse().ok()?,
            ask_notional: self.a.parse().ok()?,
            is_maker: self.m,
            reduce_only: self.R,
            position_side: self.ps,
            realized_pnl: self.rp.parse().unwrap_or_else(|_| Decimal::ZERO),
        })
    }
}

impl OrderUpdateRaw {
    pub fn to_model(self) -> Option<OrderUpdateEvent> {
        Some(OrderUpdateEvent {
            event_time: self.E,
            transaction_time: self.T,
            detail: self.o.to_model()?,
        })
    }
}

#[derive(Debug, Clone)]
pub struct OrderUpdateDetail {
    pub symbol: String,
    pub client_order_id: String,
    pub side: String,
    pub order_type: String,
    pub time_in_force: String,
    pub order_qty: Decimal,
    pub order_price: Decimal,
    pub avg_price: Decimal,
    pub stop_price: Decimal,
    pub execution_type: String,
    pub status: String,
    pub order_id: u64,
    pub last_filled_qty: Decimal,
    pub cum_filled_qty: Decimal,
    pub last_filled_price: Decimal,
    pub commission: Decimal,
    pub commission_asset: Option<String>,
    pub trade_time: u64,
    pub trade_id: i64,
    pub bid_notional: Decimal,
    pub ask_notional: Decimal,
    pub is_maker: bool,
    pub reduce_only: bool,
    pub position_side: String,
    pub realized_pnl: Decimal,
}

#[derive(Debug, Clone)]
pub struct OrderUpdateEvent {
    pub event_time: u64,
    pub transaction_time: u64,
    pub detail: OrderUpdateDetail,
}

/// ========================
/// MarginCallEvent（三段）
/// ========================

#[derive(Debug, Deserialize)]
pub struct MarginCallRaw {
    pub e: String,
    pub E: u64,
    #[serde(default)]
    pub cw: Option<String>,
    #[serde(default)]
    pub p: Vec<MarginCallPositionRaw>,
}

#[derive(Debug, Deserialize)]
pub struct MarginCallPositionRaw {
    pub s: String,
    pub ps: String,
    pub pa: String,
    pub mt: String,
    pub iw: String,
    pub mp: String,
    pub up: String,
    pub mm: String,
}

impl MarginCallPositionRaw {
    pub fn to_model(self) -> Option<MarginCallPosition> {
        Some(MarginCallPosition {
            symbol: self.s,
            position_side: self.ps,
            position_amt: self.pa.parse().ok()?,
            margin_type: self.mt,
            isolated_wallet: self.iw.parse().ok()?,
            mark_price: self.mp.parse().ok()?,
            unrealized_pnl: self.up.parse().ok()?,
            maint_margin: self.mm.parse().ok()?,
        })
    }
}

impl MarginCallRaw {
    pub fn to_model(self) -> Option<MarginCallEvent> {
        Some(MarginCallEvent {
            event_time: self.E,
            cross_wallet: self.cw.and_then(|v| v.parse().ok()),
            positions: self.p.into_iter().filter_map(|x| x.to_model()).collect(),
        })
    }
}

#[derive(Debug, Clone)]
pub struct MarginCallPosition {
    pub symbol: String,
    pub position_side: String,
    pub position_amt: Decimal,
    pub margin_type: String,
    pub isolated_wallet: Decimal,
    pub mark_price: Decimal,
    pub unrealized_pnl: Decimal,
    pub maint_margin: Decimal,
}

#[derive(Debug, Clone)]
pub struct MarginCallEvent {
    pub event_time: u64,
    pub cross_wallet: Option<Decimal>,
    pub positions: Vec<MarginCallPosition>,
}

/// ========================
/// AccountConfigUpdateEvent（三段）
/// ========================

#[derive(Debug, Deserialize)]
pub struct AccountConfigUpdateRaw {
    pub e: String,
    pub E: u64,
    pub T: u64,
    #[serde(default)]
    pub ac: Option<AccountConfigSymbolRaw>,
    #[serde(default)]
    pub ai: Option<AccountConfigInfoRaw>,
}

#[derive(Debug, Deserialize)]
pub struct AccountConfigSymbolRaw {
    pub s: String,
    pub l: u32,
}

#[derive(Debug, Deserialize)]
pub struct AccountConfigInfoRaw {
    pub j: bool,
}

impl AccountConfigSymbolRaw {
    pub fn to_model(self) -> Option<AccountSymbolConfig> {
        Some(AccountSymbolConfig {
            symbol: self.s,
            leverage: self.l,
        })
    }
}

impl AccountConfigInfoRaw {
    pub fn to_model(self) -> Option<AccountInfoConfig> {
        Some(AccountInfoConfig {
            multi_asset_mode: self.j,
        })
    }
}

impl AccountConfigUpdateRaw {
    pub fn to_model(self) -> Option<AccountConfigUpdateEvent> {
        Some(AccountConfigUpdateEvent {
            event_time: self.E,
            transaction_time: self.T,
            symbol_config: self.ac.and_then(|x| x.to_model()),
            account_info: self.ai.and_then(|x| x.to_model()),
        })
    }
}

#[derive(Debug, Clone)]
pub struct AccountSymbolConfig {
    pub symbol: String,
    pub leverage: u32,
}

#[derive(Debug, Clone)]
pub struct AccountInfoConfig {
    pub multi_asset_mode: bool,
}

#[derive(Debug, Clone)]
pub struct AccountConfigUpdateEvent {
    pub event_time: u64,
    pub transaction_time: u64,
    pub symbol_config: Option<AccountSymbolConfig>,
    pub account_info: Option<AccountInfoConfig>,
}
